tests.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. """Built-in template tests used with the ``is`` operator."""
  2. import operator
  3. import typing as t
  4. from collections import abc
  5. from numbers import Number
  6. from .runtime import Undefined
  7. from .utils import pass_environment
  8. if t.TYPE_CHECKING:
  9. from .environment import Environment
  10. def test_odd(value: int) -> bool:
  11. """Return true if the variable is odd."""
  12. return value % 2 == 1
  13. def test_even(value: int) -> bool:
  14. """Return true if the variable is even."""
  15. return value % 2 == 0
  16. def test_divisibleby(value: int, num: int) -> bool:
  17. """Check if a variable is divisible by a number."""
  18. return value % num == 0
  19. def test_defined(value: t.Any) -> bool:
  20. """Return true if the variable is defined:
  21. .. sourcecode:: jinja
  22. {% if variable is defined %}
  23. value of variable: {{ variable }}
  24. {% else %}
  25. variable is not defined
  26. {% endif %}
  27. See the :func:`default` filter for a simple way to set undefined
  28. variables.
  29. """
  30. return not isinstance(value, Undefined)
  31. def test_undefined(value: t.Any) -> bool:
  32. """Like :func:`defined` but the other way round."""
  33. return isinstance(value, Undefined)
  34. @pass_environment
  35. def test_filter(env: "Environment", value: str) -> bool:
  36. """Check if a filter exists by name. Useful if a filter may be
  37. optionally available.
  38. .. code-block:: jinja
  39. {% if 'markdown' is filter %}
  40. {{ value | markdown }}
  41. {% else %}
  42. {{ value }}
  43. {% endif %}
  44. .. versionadded:: 3.0
  45. """
  46. return value in env.filters
  47. @pass_environment
  48. def test_test(env: "Environment", value: str) -> bool:
  49. """Check if a test exists by name. Useful if a test may be
  50. optionally available.
  51. .. code-block:: jinja
  52. {% if 'loud' is test %}
  53. {% if value is loud %}
  54. {{ value|upper }}
  55. {% else %}
  56. {{ value|lower }}
  57. {% endif %}
  58. {% else %}
  59. {{ value }}
  60. {% endif %}
  61. .. versionadded:: 3.0
  62. """
  63. return value in env.tests
  64. def test_none(value: t.Any) -> bool:
  65. """Return true if the variable is none."""
  66. return value is None
  67. def test_boolean(value: t.Any) -> bool:
  68. """Return true if the object is a boolean value.
  69. .. versionadded:: 2.11
  70. """
  71. return value is True or value is False
  72. def test_false(value: t.Any) -> bool:
  73. """Return true if the object is False.
  74. .. versionadded:: 2.11
  75. """
  76. return value is False
  77. def test_true(value: t.Any) -> bool:
  78. """Return true if the object is True.
  79. .. versionadded:: 2.11
  80. """
  81. return value is True
  82. # NOTE: The existing 'number' test matches booleans and floats
  83. def test_integer(value: t.Any) -> bool:
  84. """Return true if the object is an integer.
  85. .. versionadded:: 2.11
  86. """
  87. return isinstance(value, int) and value is not True and value is not False
  88. # NOTE: The existing 'number' test matches booleans and integers
  89. def test_float(value: t.Any) -> bool:
  90. """Return true if the object is a float.
  91. .. versionadded:: 2.11
  92. """
  93. return isinstance(value, float)
  94. def test_lower(value: str) -> bool:
  95. """Return true if the variable is lowercased."""
  96. return str(value).islower()
  97. def test_upper(value: str) -> bool:
  98. """Return true if the variable is uppercased."""
  99. return str(value).isupper()
  100. def test_string(value: t.Any) -> bool:
  101. """Return true if the object is a string."""
  102. return isinstance(value, str)
  103. def test_mapping(value: t.Any) -> bool:
  104. """Return true if the object is a mapping (dict etc.).
  105. .. versionadded:: 2.6
  106. """
  107. return isinstance(value, abc.Mapping)
  108. def test_number(value: t.Any) -> bool:
  109. """Return true if the variable is a number."""
  110. return isinstance(value, Number)
  111. def test_sequence(value: t.Any) -> bool:
  112. """Return true if the variable is a sequence. Sequences are variables
  113. that are iterable.
  114. """
  115. try:
  116. len(value)
  117. value.__getitem__
  118. except Exception:
  119. return False
  120. return True
  121. def test_sameas(value: t.Any, other: t.Any) -> bool:
  122. """Check if an object points to the same memory address than another
  123. object:
  124. .. sourcecode:: jinja
  125. {% if foo.attribute is sameas false %}
  126. the foo attribute really is the `False` singleton
  127. {% endif %}
  128. """
  129. return value is other
  130. def test_iterable(value: t.Any) -> bool:
  131. """Check if it's possible to iterate over an object."""
  132. try:
  133. iter(value)
  134. except TypeError:
  135. return False
  136. return True
  137. def test_escaped(value: t.Any) -> bool:
  138. """Check if the value is escaped."""
  139. return hasattr(value, "__html__")
  140. def test_in(value: t.Any, seq: t.Container) -> bool:
  141. """Check if value is in seq.
  142. .. versionadded:: 2.10
  143. """
  144. return value in seq
  145. TESTS = {
  146. "odd": test_odd,
  147. "even": test_even,
  148. "divisibleby": test_divisibleby,
  149. "defined": test_defined,
  150. "undefined": test_undefined,
  151. "filter": test_filter,
  152. "test": test_test,
  153. "none": test_none,
  154. "boolean": test_boolean,
  155. "false": test_false,
  156. "true": test_true,
  157. "integer": test_integer,
  158. "float": test_float,
  159. "lower": test_lower,
  160. "upper": test_upper,
  161. "string": test_string,
  162. "mapping": test_mapping,
  163. "number": test_number,
  164. "sequence": test_sequence,
  165. "iterable": test_iterable,
  166. "callable": callable,
  167. "sameas": test_sameas,
  168. "escaped": test_escaped,
  169. "in": test_in,
  170. "==": operator.eq,
  171. "eq": operator.eq,
  172. "equalto": operator.eq,
  173. "!=": operator.ne,
  174. "ne": operator.ne,
  175. ">": operator.gt,
  176. "gt": operator.gt,
  177. "greaterthan": operator.gt,
  178. "ge": operator.ge,
  179. ">=": operator.ge,
  180. "<": operator.lt,
  181. "lt": operator.lt,
  182. "lessthan": operator.lt,
  183. "<=": operator.le,
  184. "le": operator.le,
  185. }